function _getProvidersTable() {
if (_providersTable)
return _providersTable;
- let [providers, countryCodes] = Shell.mobile_providers_parse();
- return _providersTable = providers;
+ return _providersTable = Shell.mobile_providers_parse(null,null);
+}
+
+function findProviderForMCCMNC(table, needle) {
+ let needlemcc = needle.substring(0, 3);
+ let needlemnc = needle.substring(3, needle.length);
+
+ let name2, name3;
+ for (let iter in table) {
+ let country = table[iter];
+ let providers = country.get_providers();
+
+ // Search through each country's providers
+ for (let i = 0; i < providers.length; i++) {
+ let provider = providers[i];
+
+ // Search through MCC/MNC list
+ let list = provider.get_gsm_mcc_mnc();
+ for (let j = 0; j < list.length; j++) {
+ let mccmnc = list[j];
+
+ // Match both 2-digit and 3-digit MNC; prefer a
+ // 3-digit match if found, otherwise a 2-digit one.
+ if (mccmnc.mcc != needlemcc)
+ continue; // MCC was wrong
+
+ if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
+ name3 = provider.name;
+
+ if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
+ name2 = provider.name;
+
+ if (name2 && name3)
+ break;
+ }
+ }
+ }
+
+ return name3 || name2 || null;
+}
+
+function findProviderForSid(table, sid) {
+ if (sid == 0)
+ return null;
+
+ // Search through each country
+ for (let iter in table) {
+ let country = table[iter];
+ let providers = country.get_providers();
+
+ // Search through each country's providers
+ for (let i = 0; i < providers.length; i++) {
+ let provider = providers[i];
+ let cdma_sid = provider.get_cdma_sid();
+
+ // Search through CDMA SID list
+ for (let j = 0; j < cdma_sid.length; j++) {
+ if (cdma_sid[j] == sid)
+ return provider.name;
+ }
+ }
+ }
+
+ return null;
}
const ModemGsm = new Lang.Class({
},
_findOperatorName: function(name, opCode) {
- if (name.length != 0 && (name.length > 6 || name.length < 5)) {
- // this looks like a valid name, i.e. not an MCCMNC (that some
- // devices return when not yet connected
- return name;
- }
- if (isNaN(parseInt(name))) {
- // name is definitely not a MCCMNC, so it may be a name
- // after all; return that
- return name;
+ if (name) {
+ if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
+ // this looks like a valid name, i.e. not an MCCMNC (that some
+ // devices return when not yet connected
+ return name;
+ }
+ if (isNaN(parseInt(name))) {
+ // name is definitely not a MCCMNC, so it may be a name
+ // after all; return that
+ return name;
+ }
}
let needle;
- if (name.length == 0 && opCode)
+ if ((name == null || name.length == 0) && opCode)
needle = opCode;
else if (name.length == 6 || name.length == 5)
needle = name;
else // nothing to search
return null;
- return this._findProviderForMCCMNC(needle);
- },
-
- _findProviderForMCCMNC: function(needle) {
let table = _getProvidersTable();
- let needlemcc = needle.substring(0, 3);
- let needlemnc = needle.substring(3, needle.length);
-
- let name2, name3;
- for (let iter in table) {
- let providers = table[iter];
-
- // Search through each country's providers
- for (let i = 0; i < providers.length; i++) {
- let provider = providers[i];
-
- // Search through MCC/MNC list
- let list = provider.get_gsm_mcc_mnc();
- for (let j = 0; j < list.length; j++) {
- let mccmnc = list[j];
-
- // Match both 2-digit and 3-digit MNC; prefer a
- // 3-digit match if found, otherwise a 2-digit one.
- if (mccmnc.mcc != needlemcc)
- continue; // MCC was wrong
-
- if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
- name3 = provider.name;
-
- if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
- name2 = provider.name;
-
- if (name2 && name3)
- break;
- }
- }
- }
-
- return name3 || name2 || null;
+ return findProviderForMCCMNC(table, needle);
}
});
Signals.addSignalMethods(ModemGsm.prototype);
this.operator_name = null;
} else {
let [bandClass, band, id] = result;
- if (name.length > 0)
- this.operator_name = this._findProviderForSid(id);
- else
+ if (name.length > 0) {
+ let table = _getProvidersTable();
+ this.operator_name = findProviderForSid(table, id);
+ } else
this.operator_name = null;
}
this.emit('notify::operator-name');
}));
- },
-
- _findProviderForSid: function(sid) {
- if (sid == 0)
- return null;
-
- let table = _getProvidersTable();
-
- // Search through each country
- for (let iter in table) {
- let providers = table[iter];
-
- // Search through each country's providers
- for (let i = 0; i < providers.length; i++) {
- let provider = providers[i];
- let cdma_sid = provider.get_cdma_sid();
-
- // Search through CDMA SID list
- for (let j = 0; j < cdma_sid.length; j++) {
- if (cdma_sid[j] == sid)
- return provider.name;
- }
- }
- }
-
- return null;
}
});
Signals.addSignalMethods(ModemCdma.prototype);
#define ISO_3166_COUNTRY_CODES DATADIR "/zoneinfo/iso3166.tab"
-
+static ShellCountryMobileProvider *country_mobile_provider_new (const char *country_code,
+ const gchar *country_name);
static GHashTable *
-read_country_codes (void)
+read_country_codes (const gchar *country_codes)
{
GHashTable *table;
GIOChannel *channel;
GError *error = NULL;
GIOStatus status;
- channel = g_io_channel_new_file (ISO_3166_COUNTRY_CODES, "r", &error);
+ channel = g_io_channel_new_file (country_codes, "r", &error);
if (!channel) {
if (error) {
- g_warning ("Could not read " ISO_3166_COUNTRY_CODES ": %s", error->message);
+ g_warning ("Could not read %s: %s", country_codes, error->message);
g_error_free (error);
} else
- g_warning ("Could not read " ISO_3166_COUNTRY_CODES ": Unknown error");
+ g_warning ("Could not read %s: Unknown error", country_codes);
return NULL;
}
- table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify)shell_country_mobile_provider_unref);
buffer = g_string_sized_new (32);
status = G_IO_STATUS_NORMAL;
switch (status) {
case G_IO_STATUS_NORMAL:
if (buffer->str[0] != '#') {
+ ShellCountryMobileProvider *country_provider;
char **pieces;
pieces = g_strsplit (buffer->str, "\t", 2);
pieces[1] = g_strdup (_("United Kingdom"));
}
- g_hash_table_insert (table, pieces[0], pieces[1]);
+ country_provider = country_mobile_provider_new (pieces[0], pieces[1]);
+ g_hash_table_insert (table, pieces[0], country_provider);
g_free (pieces);
}
} MobileContextState;
typedef struct {
- GHashTable *country_codes;
GHashTable *table;
char *current_country;
return type;
}
+static ShellCountryMobileProvider *
+country_mobile_provider_new (const char *country_code,
+ const gchar *country_name)
+{
+ ShellCountryMobileProvider *country_provider;
+
+ country_provider = g_slice_new0 (ShellCountryMobileProvider);
+ country_provider->refs = 1;
+ country_provider->country_code = g_strdup (country_code);
+ country_provider->country_name = g_strdup (country_name);
+ return country_provider;
+}
+
+ShellCountryMobileProvider *
+shell_country_mobile_provider_ref (ShellCountryMobileProvider *country_provider)
+{
+ country_provider->refs++;
+
+ return country_provider;
+}
+
+void
+shell_country_mobile_provider_unref (ShellCountryMobileProvider *country_provider)
+{
+ if (--country_provider->refs == 0) {
+ g_free (country_provider->country_code);
+ g_free (country_provider->country_name);
+ g_slist_free_full (country_provider->providers,
+ (GDestroyNotify) shell_mobile_provider_unref);
+ g_slice_free (ShellCountryMobileProvider, country_provider);
+ }
+}
+
+/**
+ * shell_country_mobile_provider_get_country_code:
+ *
+ * Returns: (transfer none): the code of the country.
+ */
+const gchar *
+shell_country_mobile_provider_get_country_code (ShellCountryMobileProvider *country_provider)
+{
+ return country_provider->country_code;
+}
+
+/**
+ * shell_country_mobile_provider_get_country_name:
+ *
+ * Returns: (transfer none): the name of the country.
+ */
+const gchar *
+shell_country_mobile_provider_get_country_name (ShellCountryMobileProvider *country_provider)
+{
+ return country_provider->country_name;
+}
+
+/**
+ * shell_country_mobile_provider_get_providers:
+ *
+ * Returns: (element-type Shell.MobileProvider) (transfer none): the
+ * list of #ShellMobileProvider this country exposes.
+ */
+GSList *
+shell_country_mobile_provider_get_providers (ShellCountryMobileProvider *country_provider)
+{
+ return country_provider->providers;
+}
+
+GType
+shell_country_mobile_provider_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ type = g_boxed_type_register_static ("ShellCountryMobileProvider",
+ (GBoxedCopyFunc) shell_country_mobile_provider_ref,
+ (GBoxedFreeFunc) shell_country_mobile_provider_unref);
+ }
+ return type;
+}
+
static void
provider_list_free (gpointer data)
{
for (i = 0; attribute_names && attribute_names[i]; i++) {
if (!strcmp (attribute_names[i], "code")) {
char *country_code;
- char *country;
+ ShellCountryMobileProvider *country_provider;
country_code = g_ascii_strup (attribute_values[i], -1);
- country = g_hash_table_lookup (parser->country_codes, country_code);
- if (country) {
- parser->current_country = g_strdup (country);
- g_free (country_code);
- } else
- parser->current_country = country_code;
+ country_provider = g_hash_table_lookup (parser->table, country_code);
+ /* Ensure we have a country provider for this country code */
+ if (!country_provider) {
+ g_warning ("%s: adding providers for unknown country '%s'", __func__, country_code);
+ country_provider = country_mobile_provider_new (country_code, NULL);
+ g_hash_table_insert (parser->table, country_code, country_provider);
+ }
+ parser->current_country = country_code;
parser->state = PARSER_COUNTRY;
break;
const char *name)
{
if (!strcmp (name, "country")) {
- g_hash_table_insert (parser->table, parser->current_country, parser->current_providers);
+ ShellCountryMobileProvider *country_provider;
+
+ country_provider = g_hash_table_lookup (parser->table, parser->current_country);
+ if (country_provider)
+ /* Store providers for this country */
+ country_provider->providers = parser->current_providers;
+
parser->current_country = NULL;
parser->current_providers = NULL;
parser->text_buffer = NULL;
/**
* shell_mobile_providers_parse:
- * @out_ccs: (out) (allow-none): (element-type utf8 utf8): a #GHashTable containing
- * country codes
+ * @country_codes: (allow-none) File with the list of country codes.
+ * @service_providers: (allow-none) File with the list of service providers.
*
- * Returns: (element-type utf8 GList) (transfer container): a
- * hash table where keys are country names #gchar, values are a #GSList
- * of #ShellMobileProvider. Everything is destroyed with g_hash_table_destroy().
-*/
+ * Returns: (element-type utf8 Shell.CountryMobileProvider) (transfer full): a
+ * hash table where keys are country names #gchar and values are #ShellCountryMobileProvider.
+ * Everything is destroyed with g_hash_table_destroy().
+ */
GHashTable *
-shell_mobile_providers_parse (GHashTable **out_ccs)
+shell_mobile_providers_parse (const gchar *country_codes,
+ const gchar *service_providers)
{
GMarkupParseContext *ctx;
GIOChannel *channel;
GIOStatus status;
gsize len = 0;
+ /* Use default paths if none given */
+ if (!country_codes)
+ country_codes = ISO_3166_COUNTRY_CODES;
+ if (!service_providers)
+ service_providers = MOBILE_BROADBAND_PROVIDER_INFO;
+
memset (&parser, 0, sizeof (MobileParser));
- parser.country_codes = read_country_codes ();
- if (!parser.country_codes)
+ parser.table = read_country_codes (country_codes);
+ if (!parser.table)
goto out;
- channel = g_io_channel_new_file (MOBILE_BROADBAND_PROVIDER_INFO, "r", &error);
+ channel = g_io_channel_new_file (service_providers, "r", &error);
if (!channel) {
if (error) {
- g_warning ("Could not read " MOBILE_BROADBAND_PROVIDER_INFO ": %s", error->message);
+ g_warning ("Could not read %s: %s", service_providers, error->message);
g_error_free (error);
} else
- g_warning ("Could not read " MOBILE_BROADBAND_PROVIDER_INFO ": Unknown error");
+ g_warning ("Could not read %s: Unknown error", service_providers);
goto out;
}
- parser.table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, provider_list_free);
parser.state = PARSER_TOPLEVEL;
ctx = g_markup_parse_context_new (&mobile_parser, 0, &parser, NULL);
g_free (parser.current_country);
g_free (parser.text_buffer);
- out:
- if (parser.country_codes) {
- if (out_ccs)
- *out_ccs = parser.country_codes;
- else
- g_hash_table_destroy (parser.country_codes);
- }
+out:
return parser.table;
}
static void
dump_country (gpointer key, gpointer value, gpointer user_data)
{
- GSList *citer, *miter;
+ GSList *miter, *citer;
+ ShellCountryMobileProvider *country_provider = value;
+
+ g_print ("Country: %s (%s)\n",
+ country_provider->country_code,
+ country_provider->country_name);
- for (citer = value; citer; citer = g_slist_next (citer)) {
+ for (citer = country_provider->providers; citer; citer = g_slist_next (citer)) {
ShellMobileProvider *provider = citer->data;
- g_print ("Provider: %s (%s)\n", provider->name, (const char *) key);
+ g_print (" Provider: %s (%s)\n", provider->name, (const char *) key);
for (miter = provider->methods; miter; miter = g_slist_next (miter)) {
ShellMobileAccessMethod *method = miter->data;
GSList *liter;
}
void
-shell_mobile_providers_dump (GHashTable *providers)
+shell_mobile_providers_dump (GHashTable *country_providers)
{
- g_return_if_fail (providers != NULL);
- g_hash_table_foreach (providers, dump_country, NULL);
+ g_return_if_fail (country_providers != NULL);
+ g_hash_table_foreach (country_providers, dump_country, NULL);
}
/* All the following don't exist in nm-applet, because C doesn't need
gint refs;
} ShellMobileProvider;
+typedef struct {
+ char *country_code;
+ char *country_name;
+ GSList *providers;
+
+ gint refs;
+} ShellCountryMobileProvider;
+
GType shell_gsm_mcc_mnc_get_type (void); /* added in porting */
-GType shell_mobile_provider_get_type (void);
GType shell_mobile_access_method_get_type (void);
+GType shell_mobile_provider_get_type (void);
+GType shell_country_mobile_provider_get_type (void);
ShellMobileProvider *shell_mobile_provider_ref (ShellMobileProvider *provider);
void shell_mobile_provider_unref (ShellMobileProvider *provider);
ShellMobileAccessMethod *shell_mobile_access_method_ref (ShellMobileAccessMethod *method);
void shell_mobile_access_method_unref (ShellMobileAccessMethod *method);
-GHashTable *shell_mobile_providers_parse (GHashTable **out_ccs);
+ShellCountryMobileProvider *shell_country_mobile_provider_ref (ShellCountryMobileProvider *country_provider);
+void shell_country_mobile_provider_unref (ShellCountryMobileProvider *country_provider);
+const gchar *shell_country_mobile_provider_get_country_code (ShellCountryMobileProvider *country_provider);
+const gchar *shell_country_mobile_provider_get_country_name (ShellCountryMobileProvider *country_provider);
+GSList *shell_country_mobile_provider_get_providers (ShellCountryMobileProvider *country_provider);
-void shell_mobile_providers_dump (GHashTable *providers);
+GHashTable *shell_mobile_providers_parse (const gchar *country_codes,
+ const gchar *service_providers);
+void shell_mobile_providers_dump (GHashTable *country_providers);
#endif /* SHELL_MOBILE_PROVIDERS_H */
testcommon/border-image.png \
testcommon/face-plain.png \
testcommon/ui.js \
+ testcommon/iso3166-test.tab \
+ testcommon/serviceproviders-test.xml \
unit/format.js \
unit/insertSorted.js \
unit/markup.js \
unit/jsParse.js \
- unit/url.js
+ unit/url.js \
+ unit/mobileProviders.js
EXTRA_DIST += $(TEST_JS)
TEST_MISC = \
--- /dev/null
+# Test country list
+ES Spain
+US United States
\ No newline at end of file
--- /dev/null
+<?xml version="1.0"?>
+
+<!-- -*- Mode: XML; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- -->
+
+<!DOCTYPE serviceproviders SYSTEM "serviceproviders.2.dtd">
+
+<serviceproviders format="2.0">
+
+ <!-- United States -->
+ <country code="us">
+ <provider>
+ <name>AT&T</name>
+ <gsm>
+ <network-id mcc="310" mnc="038"/>
+ <network-id mcc="310" mnc="090"/>
+ <network-id mcc="310" mnc="150"/>
+ </gsm>
+ </provider>
+ <provider>
+ <name>Verizon</name>
+ <gsm>
+ <network-id mcc="310" mnc="995"/>
+ <network-id mcc="311" mnc="480"/>
+ </gsm>
+ <cdma>
+ <sid value="2"/>
+ <sid value="4"/>
+ <sid value="5"/>
+ </cdma>
+ </provider>
+ </country>
+
+ <!-- Spain -->
+ <country code="es">
+ <provider>
+ <name>Movistar (Telefónica)</name>
+ <gsm>
+ <network-id mcc="214" mnc="05"/>
+ <network-id mcc="214" mnc="07"/>
+ </gsm>
+ </provider>
+ </country>
+
+</serviceproviders>
--- /dev/null
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const GLib = imports.gi.GLib;
+const Shell = imports.gi.Shell;
+const JsUnit = imports.jsUnit;
+const ModemManager = imports.misc.modemManager;
+const Environment = imports.ui.environment;
+
+Environment.init();
+
+// Load test providers table
+let countrycodesPath = GLib.getenv("GNOME_SHELL_TESTSDIR") + "/testcommon/iso3166-test.tab";
+let serviceprovidersPath = GLib.getenv("GNOME_SHELL_TESTSDIR") + "/testcommon/serviceproviders-test.xml";
+let providersTable = Shell.mobile_providers_parse(countrycodesPath, serviceprovidersPath);
+
+function assertCountryFound(country_code, expected_country_name) {
+ let country = providersTable[country_code];
+ JsUnit.assertNotUndefined(country);
+ JsUnit.assertEquals(country.get_country_name(), expected_country_name);
+}
+
+function assertCountryNotFound(country_code) {
+ let country = providersTable[country_code];
+ JsUnit.assertUndefined(country);
+}
+
+function assertProviderFoundForMCCMNC(mccmnc, expected_provider_name) {
+ let provider_name = ModemManager.findProviderForMCCMNC(providersTable, mccmnc);
+ JsUnit.assertEquals(provider_name, expected_provider_name);
+}
+
+function assertProviderNotFoundForMCCMNC(mccmnc) {
+ let provider_name = ModemManager.findProviderForMCCMNC(providersTable, mccmnc);
+ JsUnit.assertNull(provider_name);
+}
+
+function assertProviderFoundForSid(sid, expected_provider_name) {
+ let provider_name = ModemManager.findProviderForSid(providersTable, sid);
+ JsUnit.assertEquals(provider_name, expected_provider_name);
+}
+
+function assertProviderNotFoundForSid(sid) {
+ let provider_name = ModemManager.findProviderForSid(providersTable, sid);
+ JsUnit.assertNull(provider_name);
+}
+
+// TEST:
+// * Both 'US' and 'ES' country info should be loaded
+assertCountryFound("ES", "Spain");
+assertCountryFound("US", "United States");
+
+// TEST:
+// * Country info for 'FR' not given
+assertCountryNotFound("FR");
+
+// TEST:
+// * Ensure operator names are found for the given MCC/MNC codes
+assertProviderFoundForMCCMNC("21405", "Movistar (Telefónica)");
+assertProviderFoundForMCCMNC("21407", "Movistar (Telefónica)");
+assertProviderFoundForMCCMNC("310038", "AT&T");
+assertProviderFoundForMCCMNC("310090", "AT&T");
+assertProviderFoundForMCCMNC("310150", "AT&T");
+assertProviderFoundForMCCMNC("310995", "Verizon");
+assertProviderFoundForMCCMNC("311480", "Verizon");
+
+// TEST:
+// * Ensure NULL is given for unexpected MCC/MNC codes
+assertProviderNotFoundForMCCMNC("12345");
+
+// TEST:
+// * Ensure operator names are found for the given SID codes
+assertProviderFoundForSid(2, "Verizon");
+assertProviderFoundForSid(4, "Verizon");
+assertProviderFoundForSid(5, "Verizon");
+
+// TEST:
+// * Ensure NULL is given for unexpected SID codes
+assertProviderNotFoundForSid(1);